#include "global.h"
#include "item.h"
#include "berry.h"
#include "string_util.h"
#include "text.h"
#include "event_data.h"
#include "malloc.h"
#include "secret_base.h"
#include "item_menu.h"
#include "strings.h"
#include "load_save.h"
#include "item_use.h"
#include "battle_pyramid.h"
#include "battle_pyramid_bag.h"
#include "constants/items.h"
#include "constants/hold_effects.h"
#include "constants/tv.h"

extern u16 gUnknown_0203CF30[];

#define NONMATCHING

// this file's functions
#if !defined(NONMATCHING) && MODERN
#define static
#endif
static bool8 CheckPyramidBagHasItem(u16 itemId, u16 count);
static bool8 CheckPyramidBagHasSpace(u16 itemId, u16 count);

// EWRAM variables
EWRAM_DATA struct BagPocket gBagQuantity = {0};

// rodata
#include "data/text/item_descriptions.h"
#include "data/items.h"

// code
u16 GetBagItemQuantity(u16 itemId)
{
    if (gBagQuantity.item[itemId].magic1 != 1 || gBagQuantity.item[itemId].magic2 != 1)
        gBagQuantity.item[itemId].quatity = 0;
    return gBagQuantity.item[itemId].quatity;
}

static void SetBagItemQuantity(u16 itemId, u16 newValue)
{
    gBagQuantity.item[itemId].magic1 = gBagQuantity.item[itemId].magic2 = 1;
    gBagQuantity.item[itemId].quatity = newValue;
}

static u16 GetPCItemQuantity(u16 *quantity)
{
    return *quantity;
}

static void SetPCItemQuantity(u16 *quantity, u16 newValue)
{
    *quantity = newValue;
}

void ApplyNewEncryptionKeyToBagItems(u32 newKey)
{
//    u32 pocket, item;
//    for (pocket = 0; pocket < POCKETS_COUNT; pocket++)
//    {
//        for (item = 0; item < gBagPockets[pocket].capacity; item++)
//            ApplyNewEncryptionKeyToHword(&(gBagPockets[pocket].itemSlots[item].quantity), newKey);
//    }
}

void ApplyNewEncryptionKeyToBagItems_(u32 newKey) // really GF?
{
    ApplyNewEncryptionKeyToBagItems(newKey);
}

void SetBagItemsPointers(void)
{
    gBagQuantity.item = gSaveBlock1Ptr->quantity;
    gBagQuantity.itemBag = NULL;
}

void CopyItemName(u16 itemId, u8 *dst)
{
    StringCopy(dst, ItemId_GetName(itemId));
}

void CopyItemNameHandlePlural(u16 itemId, u8 *dst, u32 quantity)
{
    if (itemId == ITEM_POKE_BALL)
    {
        if (quantity < 2)
            StringCopy(dst, ItemId_GetName(ITEM_POKE_BALL));
        else
            StringCopy(dst, gText_PokeBalls);
    }
    else
    {
        if (itemId >= ITEM_CHERI_BERRY && itemId <= ITEM_ENIGMA_BERRY)
            GetBerryCountString(dst, gBerries[itemId - ITEM_CHERI_BERRY].name, quantity);
        else
            StringCopy(dst, ItemId_GetName(itemId));
    }
}

void GetBerryCountString(u8 *dst, const u8 *berryName, u32 quantity)
{
    const u8 *berryString;
    u8 *txtPtr;

    if (quantity < 2)
        berryString = gText_Berry;
    else
        berryString = gText_Berries;

    txtPtr = StringCopy(dst, berryName);
    *txtPtr = CHAR_SPACE;
    StringCopy(txtPtr + 1, berryString);
}

bool8 IsBagPocketNonEmpty(u8 pocket)
{
    u32 i;

    for (i = 0; i < BAG_ALL_ITEMS_COUNT; i++)
    {
        if (GetBagItemQuantity(i))
            return TRUE;
    }
    return FALSE;
}

bool8 CheckBagHasItem(u16 itemId, u16 count)
{
    u8 i;
    u8 pocket;

    if (ItemId_GetPocket(itemId) == 0)
        return FALSE;
    if (InBattlePyramid() || FlagGet(FLAG_STORING_ITEMS_IN_PYRAMID_BAG) == TRUE)
        return CheckPyramidBagHasItem(itemId, count);

    return GetBagItemQuantity(itemId) >= count;
}

bool8 HasAtLeastOneBerry(void)
{
    u16 i;

    for (i = FIRST_BERRY_INDEX; i < ITEM_BRIGHT_POWDER; i++)
    {
        if (CheckBagHasItem(i, 1) == TRUE)
        {
            gSpecialVar_Result = TRUE;
            return TRUE;
        }
    }
    gSpecialVar_Result = FALSE;
    return FALSE;
}

bool8 HasAtLeastOneBall(void)
{
    u16 i;

    for (i = ITEM_MASTER_BALL; i <= LAST_BALL; i++)
    {
        if (CheckBagHasItem(i, 1) == TRUE)
            return TRUE;
    }
    return FALSE;
}

#ifdef NONMATCHING
// Refuses to match.
bool8 CheckBagHasSpace(u16 itemId, u16 count)
{
    u8 i;

    if (ItemId_GetPocket(itemId) == POCKET_NONE)
        return FALSE;

    if (InBattlePyramid() || FlagGet(FLAG_STORING_ITEMS_IN_PYRAMID_BAG) == TRUE)
    {
        return CheckPyramidBagHasSpace(itemId, count);
    }
    else
    {
        u8 pocket;
        u16 slotCapacity;
        u16 ownedCount;

        pocket = ItemId_GetPocket(itemId) - 1;
        if (pocket != BERRIES_POCKET)
            slotCapacity = MAX_BUY_ITEM_CAPACITY;
        else
            slotCapacity = MAX_BERRY_CAPACITY;

        return GetBagItemQuantity(itemId) + count <= slotCapacity;
    }
}
#else
NAKED
bool8 CheckBagHasSpace(u16 itemId, u16 count)
{
    asm_unified(
    "push {r4-r7,lr}\n\
    mov r7, r10\n\
    mov r6, r9\n\
    mov r5, r8\n\
    push {r5-r7}\n\
    sub sp, 0x4\n\
    lsls r0, 16\n\
    lsrs r0, 16\n\
    mov r8, r0\n\
    lsls r1, 16\n\
    lsrs r5, r1, 16\n\
    bl ItemId_GetPocket\n\
    lsls r0, 24\n\
    cmp r0, 0\n\
    beq _080D6906\n\
    bl InBattlePyramid\n\
    lsls r0, 24\n\
    cmp r0, 0\n\
    bne _080D6838\n\
    ldr r0, =0x00004004\n\
    bl FlagGet\n\
    lsls r0, 24\n\
    lsrs r0, 24\n\
    cmp r0, 0x1\n\
    bne _080D684C\n\
_080D6838:\n\
    mov r0, r8\n\
    adds r1, r5, 0\n\
    bl CheckPyramidBagHasSpace\n\
    lsls r0, 24\n\
    lsrs r0, 24\n\
    b _080D6916\n\
    .pool\n\
_080D684C:\n\
    mov r0, r8\n\
    bl ItemId_GetPocket\n\
    subs r0, 0x1\n\
    lsls r0, 24\n\
    lsrs r2, r0, 24\n\
    ldr r7, =0x000003e7\n\
    cmp r2, 0x3\n\
    beq _080D6860\n\
    movs r7, 0x63\n\
_080D6860:\n\
    movs r6, 0\n\
    ldr r1, =gBagPockets\n\
    lsls r4, r2, 3\n\
    adds r0, r4, r1\n\
    mov r9, r4\n\
    ldrb r0, [r0, 0x4]\n\
    cmp r6, r0\n\
    bcs _080D68BC\n\
    subs r0, r2, 0x2\n\
    lsls r0, 24\n\
    lsrs r0, 24\n\
    mov r10, r0\n\
_080D6878:\n\
    adds r0, r4, r1\n\
    ldr r1, [r0]\n\
    lsls r0, r6, 2\n\
    adds r1, r0, r1\n\
    ldrh r0, [r1]\n\
    cmp r0, r8\n\
    bne _080D68AC\n\
    adds r0, r1, 0x2\n\
    str r2, [sp]\n\
    bl GetBagItemQuantity\n\
    lsls r0, 16\n\
    lsrs r1, r0, 16\n\
    adds r0, r1, r5\n\
    ldr r2, [sp]\n\
    cmp r0, r7\n\
    ble _080D6914\n\
    mov r0, r10\n\
    cmp r0, 0x1\n\
    bls _080D6906\n\
    subs r0, r7, r1\n\
    subs r0, r5, r0\n\
    lsls r0, 16\n\
    lsrs r5, r0, 16\n\
    cmp r5, 0\n\
    beq _080D6914\n\
_080D68AC:\n\
    adds r0, r6, 0x1\n\
    lsls r0, 24\n\
    lsrs r6, r0, 24\n\
    ldr r1, =gBagPockets\n\
    adds r0, r4, r1\n\
    ldrb r0, [r0, 0x4]\n\
    cmp r6, r0\n\
    bcc _080D6878\n\
_080D68BC:\n\
    cmp r5, 0\n\
    beq _080D6914\n\
    movs r6, 0\n\
    ldr r3, =gBagPockets\n\
    mov r1, r9\n\
    adds r0, r1, r3\n\
    ldrb r0, [r0, 0x4]\n\
    cmp r6, r0\n\
    bcs _080D6902\n\
    adds r4, r3, 0\n\
    subs r0, r2, 0x2\n\
    lsls r0, 24\n\
    lsrs r2, r0, 24\n\
_080D68D6:\n\
    adds r0, r1, r4\n\
    ldr r1, [r0]\n\
    lsls r0, r6, 2\n\
    adds r0, r1\n\
    ldrh r0, [r0]\n\
    cmp r0, 0\n\
    bne _080D68F2\n\
    cmp r5, r7\n\
    bls _080D6914\n\
    cmp r2, 0x1\n\
    bls _080D6906\n\
    subs r0, r5, r7\n\
    lsls r0, 16\n\
    lsrs r5, r0, 16\n\
_080D68F2:\n\
    adds r0, r6, 0x1\n\
    lsls r0, 24\n\
    lsrs r6, r0, 24\n\
    mov r1, r9\n\
    adds r0, r1, r3\n\
    ldrb r0, [r0, 0x4]\n\
    cmp r6, r0\n\
    bcc _080D68D6\n\
_080D6902:\n\
    cmp r5, 0\n\
    beq _080D6914\n\
_080D6906:\n\
    movs r0, 0\n\
    b _080D6916\n\
    .pool\n\
_080D6914:\n\
    movs r0, 0x1\n\
_080D6916:\n\
    add sp, 0x4\n\
    pop {r3-r5}\n\
    mov r8, r3\n\
    mov r9, r4\n\
    mov r10, r5\n\
    pop {r4-r7}\n\
    pop {r1}\n\
    bx r1");
}
#endif // NONMATCHING

bool8 AddBagItem(u16 itemId, u16 count)
{
    u8 i;

    if (ItemId_GetPocket(itemId) == POCKET_NONE)
        return FALSE;

    // check Battle Pyramid Bag
    if (InBattlePyramid() || FlagGet(FLAG_STORING_ITEMS_IN_PYRAMID_BAG) == TRUE)
    {
        return AddPyramidBagItem(itemId, count);
    }
    else
    {
        u16 quatity = GetBagItemQuantity(itemId);
        if (quatity + count < MAX_BAG_ITEM_CAPACITY)
        {
            SetBagItemQuantity(itemId, quatity + count);
            return TRUE;
        }
        return FALSE;
    }
}

bool8 RemoveBagItem(u16 itemId, u16 count)
{
    if (ItemId_GetPocket(itemId) == POCKET_NONE || itemId == ITEM_NONE)
        return FALSE;

    // check Battle Pyramid Bag
    if (InBattlePyramid() || FlagGet(FLAG_STORING_ITEMS_IN_PYRAMID_BAG) == TRUE)
    {
        return RemovePyramidBagItem(itemId, count);
    }
    else
    {
        u16 quatity = GetBagItemQuantity(itemId);
        if (quatity > 0)
        {
            s32 result = quatity- count;
            if (result < 0)
                result = 0;
            SetBagItemQuantity(itemId, result);
            return TRUE;
        }
        return FALSE;
    }
}

u8 GetPocketByItemId(u16 itemId)
{
    return ItemId_GetPocket(itemId);
}

void ClearItemSlots(struct ItemSlot *itemSlots, u8 itemCount)
{
    u16 i;

    for (i = 0; i < itemCount; i++)
    {
        itemSlots[i].itemId = ITEM_NONE;
        SetBagItemQuantity(itemSlots[i].itemId, 0);
    }
}

static s32 FindFreePCItemSlot(void)
{
    s8 i;

    for (i = 0; i < PC_ITEMS_COUNT; i++)
    {
        if (gSaveBlock1Ptr->pcItems[i].itemId == ITEM_NONE)
            return i;
    }
    return -1;
}

u8 CountUsedPCItemSlots(void)
{
    u8 usedSlots = 0;
    u8 i;

    for (i = 0; i < PC_ITEMS_COUNT; i++)
    {
        if (gSaveBlock1Ptr->pcItems[i].itemId != ITEM_NONE)
            usedSlots++;
    }
    return usedSlots;
}

bool8 CheckPCHasItem(u16 itemId, u16 count)
{
    u8 i;

    for (i = 0; i < PC_ITEMS_COUNT; i++)
    {
        if (gSaveBlock1Ptr->pcItems[i].itemId == itemId && GetPCItemQuantity(&gSaveBlock1Ptr->pcItems[i].quantity) >= count)
            return TRUE;
    }
    return FALSE;
}

bool8 AddPCItem(u16 itemId, u16 count)
{
    u8 i;
    s8 freeSlot;
    u16 ownedCount;
    struct ItemSlot *newItems;

    // Copy PC items
    newItems = AllocZeroed(sizeof(gSaveBlock1Ptr->pcItems));
    memcpy(newItems, gSaveBlock1Ptr->pcItems, sizeof(gSaveBlock1Ptr->pcItems));

    // Use any item slots that already contain this item
    for (i = 0; i < PC_ITEMS_COUNT; i++)
    {
        if (newItems[i].itemId == itemId)
        {
            ownedCount = GetPCItemQuantity(&newItems[i].quantity);
            if (ownedCount + count <= MAX_PC_ITEM_CAPACITY)
            {
                SetPCItemQuantity(&newItems[i].quantity, ownedCount + count);
                memcpy(gSaveBlock1Ptr->pcItems, newItems, sizeof(gSaveBlock1Ptr->pcItems));
                Free(newItems);
                return TRUE;
            }
            count += ownedCount - MAX_PC_ITEM_CAPACITY;
            SetPCItemQuantity(&newItems[i].quantity, MAX_PC_ITEM_CAPACITY);
            if (count == 0)
            {
                memcpy(gSaveBlock1Ptr->pcItems, newItems, sizeof(gSaveBlock1Ptr->pcItems));
                Free(newItems);
                return TRUE;
            }
        }
    }

    // Put any remaining items into a new item slot.
    if (count > 0)
    {
        freeSlot = FindFreePCItemSlot();
        if (freeSlot == -1)
        {
            Free(newItems);
            return FALSE;
        }
        else
        {
            newItems[freeSlot].itemId = itemId;
            SetPCItemQuantity(&newItems[freeSlot].quantity, count);
        }
    }

    // Copy items back to the PC
    memcpy(gSaveBlock1Ptr->pcItems, newItems, sizeof(gSaveBlock1Ptr->pcItems));
    Free(newItems);
    return TRUE;
}

void RemovePCItem(u8 index, u16 count)
{
    // UB: should use GetPCItemQuantity and SetPCItemQuantity functions
    gSaveBlock1Ptr->pcItems[index].quantity -= count;
    if (gSaveBlock1Ptr->pcItems[index].quantity == 0)
    {
        gSaveBlock1Ptr->pcItems[index].itemId = ITEM_NONE;
        CompactPCItems();
    }
}

void CompactPCItems(void)
{
    u16 i;
    u16 j;

    for (i = 0; i < PC_ITEMS_COUNT - 1; i++)
    {
        for (j = i + 1; j < PC_ITEMS_COUNT; j++)
        {
            if (gSaveBlock1Ptr->pcItems[i].itemId == 0)
            {
                struct ItemSlot temp = gSaveBlock1Ptr->pcItems[i];
                gSaveBlock1Ptr->pcItems[i] = gSaveBlock1Ptr->pcItems[j];
                gSaveBlock1Ptr->pcItems[j] = temp;
            }
        }
    }
}

void SwapRegisteredBike(void)
{
//    switch (gSaveBlock1Ptr->registeredItem)
//    {
//    case ITEM_MACH_BIKE:
//        gSaveBlock1Ptr->registeredItem = ITEM_ACRO_BIKE;
//        break;
//    case ITEM_ACRO_BIKE:
//        gSaveBlock1Ptr->registeredItem = ITEM_MACH_BIKE;
//        break;
//    }

    u16 i;

    for (i = 0; i < REGISTERED_COUNT; i++)
    {
        switch (gSaveBlock1Ptr->registeredItems[i])
        {
        case ITEM_MACH_BIKE:
            gSaveBlock1Ptr->registeredItems[i] = ITEM_ACRO_BIKE;
            return;
        case ITEM_ACRO_BIKE:
            gSaveBlock1Ptr->registeredItems[i] = ITEM_MACH_BIKE;
            return;
        }
    }
}

u16 BagGetItemIdByPocketPosition(u8 pocketId, u16 pocketPos)
{
    return gBagQuantity.itemBag[pocketId - 1].itemSlots[pocketPos].itemId;
}

u16 BagGetQuantityByPocketPosition(u8 pocketId, u16 pocketPos)
{
    return GetBagItemQuantity(gBagQuantity.itemBag[pocketId - 1].itemSlots[pocketPos].itemId);
}

static void SwapItemSlots(struct ItemSlot *a, struct ItemSlot *b)
{
    struct ItemSlot temp;
    SWAP(*a, *b, temp);
}

void CompactItemsInBagPocket(struct BagItemQuantity *bagPocket, u8 pocketId)
{
    u16 i, j;
    for (i = 0; i < bagPocket->capacity - 1; i++)
    {
        for (j = i + 1; j < bagPocket->capacity; j++)
        {
            if (GetBagItemQuantity(bagPocket->itemSlots[i].itemId) == 0)
                SwapItemSlots(&bagPocket->itemSlots[i], &bagPocket->itemSlots[j]);
        }
    }
}

//void CompactItemsInBagPocket(struct BagItemQuantity *bagPocket, u8 pocketId)
//{
//    u16 i, j;
//
//    for (i = 0; i < bagPocket->capacity - 1; i++)
//    {
//        if (ItemId_GetPocket(bagPocket->itemSlots[i].itemId) - 1 != pocketId) //放错地方的物品
//        {
//            if (GetBagItemQuantity(bagPocket->itemSlots[i].itemId) < 16) //应该是不bug产生的东西 而是mega石之类
//            {
//                AddBagItem(bagPocket->itemSlots[i].itemId, GetBagItemQuantity(bagPocket->itemSlots[i].itemId));
//                //如果加入的包裹之前被整理过 则顺序肯定正确
//                //若没有被整理过 则在后续的整理中会被恢复正确顺序
//                //故无需递归
//            }
//            bagPocket->itemSlots[i].itemId = 0;
//            SetBagItemQuantity(bagPocket->itemSlots[i].itemId, 0);
//        }
//
//        //防止通过特殊途径非法刷取过量物品导致Bug
//        if ((pocketId == KEYITEMS_POCKET || pocketId == LEGENDARY_POCKET) &&
//            GetBagItemQuantity(bagPocket->itemSlots[i].itemId) > 1)
//            SetBagItemQuantity(bagPocket->itemSlots[i].itemId, 1);
//    }
//
//    for (i = 0; i < bagPocket->capacity - 1; i++)
//    {
//        for (j = i + 1; j < bagPocket->capacity; j++)
//        {
//            if (GetBagItemQuantity(bagPocket->itemSlots[i].itemId) == 0)
//                SwapItemSlots(&bagPocket->itemSlots[i], &bagPocket->itemSlots[j]);
//        }
//    }
//}

//void SortBerriesOrTMHMs(struct BagPocket *bagPocket, u8 pocketId)
//{
//    u16 i, j;
//
//    for (i = 0; i < bagPocket->capacity - 1; i++)
//    {
//        if (ItemId_GetPocket(bagPocket->itemSlots[i].itemId) - 1 != pocketId) //放错地方的物品
//        {
//            if (GetBagItemQuantity(&bagPocket->itemSlots[i].quantity) < 16) //应该是不bug产生的东西 而是mega石之类
//            {
//                AddBagItem(bagPocket->itemSlots[i].itemId, GetBagItemQuantity(&bagPocket->itemSlots[i].quantity));
//                //如果加入的包裹之前被整理过 则顺序肯定正确
//                //若没有被整理过 则在后续的整理中会被恢复正确顺序
//                //故无需递归
//            }
//            bagPocket->itemSlots[i].itemId = 0;
//            SetBagItemQuantity(&bagPocket->itemSlots[i].quantity, 0);
//        }
//
//        //防止通过特殊途径非法刷取过量物品导致Bug
//        if (pocketId == TMHM_POCKET && GetBagItemQuantity(&bagPocket->itemSlots[i].quantity) > 1)
//            SetBagItemQuantity(&bagPocket->itemSlots[i].quantity, 1);
//    }
//
//    for (i = 0; i < bagPocket->capacity - 1; i++)
//    {
//        for (j = i + 1; j < bagPocket->capacity; j++)
//        {
//            if (GetBagItemQuantity(&bagPocket->itemSlots[i].quantity) != 0)
//            {
//                if (GetBagItemQuantity(&bagPocket->itemSlots[j].quantity) == 0)
//                    continue;
//                if (bagPocket->itemSlots[i].itemId <= bagPocket->itemSlots[j].itemId)
//                    continue;
//            }
//            SwapItemSlots(&bagPocket->itemSlots[i], &bagPocket->itemSlots[j]);
//        }
//    }
//}

void SortByID(void)
{
//    SortBerriesOrTMHMs(&gBagPockets[gBagPositionStruct.pocket], gBagPositionStruct.pocket);
}

void SortByCount(void)
{
//    u16 i, j;
//    struct BagPocket *bagPocket = &gBagPockets[gBagPositionStruct.pocket];
//
//    for (i = 0; i < bagPocket->capacity - 1; i++)
//    {
//        for (j = i + 1; j < bagPocket->capacity; j++)
//        {
//            if (GetBagItemQuantity(&bagPocket->itemSlots[i].quantity) != 0)
//            {
//                if (GetBagItemQuantity(&bagPocket->itemSlots[j].quantity) == 0)
//                    continue;
//                if (GetBagItemQuantity(&bagPocket->itemSlots[i].quantity) >=
//                    GetBagItemQuantity(&bagPocket->itemSlots[j].quantity))
//                    continue;
//            }
//            SwapItemSlots(&bagPocket->itemSlots[i], &bagPocket->itemSlots[j]);
//        }
//    }
}

void SortByAZ(void)
{
//    u16 i, j;
//    struct BagPocket *bagPocket = &gBagPockets[gBagPositionStruct.pocket];
//
//    for (i = 0; i < bagPocket->capacity - 1; i++)
//    {
//        for (j = i + 1; j < bagPocket->capacity; j++)
//        {
//            if (GetBagItemQuantity(&bagPocket->itemSlots[i].quantity) != 0)
//            {
//                if (GetBagItemQuantity(&bagPocket->itemSlots[j].quantity) == 0)
//                    continue;
//                if (StringCompare(gItems[bagPocket->itemSlots[i].itemId].name,
//                                  gItems[bagPocket->itemSlots[j].itemId].name) < 0)
//                    continue;
//            }
//            SwapItemSlots(&bagPocket->itemSlots[i], &bagPocket->itemSlots[j]);
//        }
//    }
}

void MoveItemSlotInList(struct ItemSlot* itemSlots_, u32 from, u32 to_)
{
    // dumb assignments needed to match
    struct ItemSlot *itemSlots = itemSlots_;
    u32 to = to_;

    if (from != to)
    {
        s16 i, count;
        struct ItemSlot firstSlot = itemSlots[from];

        if (to > from)
        {
            to--;
            for (i = from, count = to; i < count; i++)
                itemSlots[i] = itemSlots[i + 1];
        }
        else
        {
            for (i = from, count = to; i > count; i--)
                itemSlots[i] = itemSlots[i - 1];
        }
        itemSlots[to] = firstSlot;
    }
}

void ClearBag(void)
{
    memset(gBagQuantity.item, 0, BAG_ALL_ITEMS_COUNT*2);
}

u16 CountTotalItemQuantityInBag(u16 itemId)
{
    return GetBagItemQuantity(itemId);
}

static bool8 CheckPyramidBagHasItem(u16 itemId, u16 count)
{
    u8 i;
    u16 *items = gSaveBlock2Ptr->frontier.pyramidBag.itemId[gSaveBlock2Ptr->frontier.lvlMode];
    u16 *quantities = gSaveBlock2Ptr->frontier.pyramidBag.quantity[gSaveBlock2Ptr->frontier.lvlMode];

    for (i = 0; i < PYRAMID_BAG_ITEMS_COUNT; i++)
    {
        if (items[i] == itemId)
        {
            if (quantities[i] >= count)
                return TRUE;

            count -= quantities[i];
            if (count == 0)
                return TRUE;
        }
    }

    return FALSE;
}

static bool8 CheckPyramidBagHasSpace(u16 itemId, u16 count)
{
    u8 i;
    u16 *items = gSaveBlock2Ptr->frontier.pyramidBag.itemId[gSaveBlock2Ptr->frontier.lvlMode];
    u16 *quantities = gSaveBlock2Ptr->frontier.pyramidBag.quantity[gSaveBlock2Ptr->frontier.lvlMode];

    for (i = 0; i < PYRAMID_BAG_ITEMS_COUNT; i++)
    {
        if (items[i] == itemId || items[i] == ITEM_NONE)
        {
            if (quantities[i] + count <= MAX_BUY_ITEM_CAPACITY)
                return TRUE;

            count = (quantities[i] + count) - MAX_BUY_ITEM_CAPACITY;
            if (count == 0)
                return TRUE;
        }
    }

    return FALSE;
}

bool8 AddPyramidBagItem(u16 itemId, u16 count)
{
    u16 i;

    u16 *items = gSaveBlock2Ptr->frontier.pyramidBag.itemId[gSaveBlock2Ptr->frontier.lvlMode];
    u16 *quantities = gSaveBlock2Ptr->frontier.pyramidBag.quantity[gSaveBlock2Ptr->frontier.lvlMode];

    u16 *newItems = Alloc(PYRAMID_BAG_ITEMS_COUNT * sizeof(u16));
    u16 *newQuantities = Alloc(PYRAMID_BAG_ITEMS_COUNT * sizeof(u16));

    memcpy(newItems, items, PYRAMID_BAG_ITEMS_COUNT * sizeof(u16));
    memcpy(newQuantities, quantities, PYRAMID_BAG_ITEMS_COUNT * sizeof(u16));

    for (i = 0; i < PYRAMID_BAG_ITEMS_COUNT; i++)
    {
        if (newItems[i] == itemId && newQuantities[i] < MAX_BUY_ITEM_CAPACITY)
        {
            newQuantities[i] += count;
            if (newQuantities[i] > MAX_BUY_ITEM_CAPACITY)
            {
                count = newQuantities[i] - MAX_BUY_ITEM_CAPACITY;
                newQuantities[i] = MAX_BUY_ITEM_CAPACITY;
            }
            else
            {
                count = 0;
            }

            if (count == 0)
                break;
        }
    }

    if (count > 0)
    {
        for (i = 0; i < PYRAMID_BAG_ITEMS_COUNT; i++)
        {
            if (newItems[i] == ITEM_NONE)
            {
                newItems[i] = itemId;
                newQuantities[i] = count;
                if (newQuantities[i] > MAX_BUY_ITEM_CAPACITY)
                {
                    count = newQuantities[i] - MAX_BUY_ITEM_CAPACITY;
                    newQuantities[i] = MAX_BUY_ITEM_CAPACITY;
                }
                else
                {
                    count = 0;
                }

                if (count == 0)
                    break;
            }
        }
    }

    if (count == 0)
    {
        memcpy(items, newItems, PYRAMID_BAG_ITEMS_COUNT * sizeof(u16));
        memcpy(quantities, newQuantities, PYRAMID_BAG_ITEMS_COUNT * sizeof(u16));
        Free(newItems);
        Free(newQuantities);
        return TRUE;
    }
    else
    {
        Free(newItems);
        Free(newQuantities);
        return FALSE;
    }
}

bool8 RemovePyramidBagItem(u16 itemId, u16 count)
{
    u16 i;

    u16 *items = gSaveBlock2Ptr->frontier.pyramidBag.itemId[gSaveBlock2Ptr->frontier.lvlMode];
    u16 *quantities = gSaveBlock2Ptr->frontier.pyramidBag.quantity[gSaveBlock2Ptr->frontier.lvlMode];

    i = gPyramidBagCursorData.cursorPosition + gPyramidBagCursorData.scrollPosition;
    if (items[i] == itemId && quantities[i] >= count)
    {
        quantities[i] -= count;
        if (quantities[i] == 0)
            items[i] = ITEM_NONE;
        return TRUE;
    }
    else
    {
        u16 *newItems = Alloc(PYRAMID_BAG_ITEMS_COUNT * sizeof(u16));
        u16 *newQuantities = Alloc(PYRAMID_BAG_ITEMS_COUNT * sizeof(u16));

        memcpy(newItems, items, PYRAMID_BAG_ITEMS_COUNT * sizeof(u16));
        memcpy(newQuantities, quantities, PYRAMID_BAG_ITEMS_COUNT * sizeof(u16));

        for (i = 0; i < PYRAMID_BAG_ITEMS_COUNT; i++)
        {
            if (newItems[i] == itemId)
            {
                if (newQuantities[i] >= count)
                {
                    newQuantities[i] -= count;
                    count = 0;
                    if (newQuantities[i] == 0)
                        newItems[i] = ITEM_NONE;
                }
                else
                {
                    count -= newQuantities[i];
                    newQuantities[i] = 0;
                    newItems[i] = ITEM_NONE;
                }

                if (count == 0)
                    break;
            }
        }

        if (count == 0)
        {
            memcpy(items, newItems, PYRAMID_BAG_ITEMS_COUNT * sizeof(u16));
            memcpy(quantities, newQuantities, PYRAMID_BAG_ITEMS_COUNT * sizeof(u16));
            Free(newItems);
            Free(newQuantities);
            return TRUE;
        }
        else
        {
            Free(newItems);
            Free(newQuantities);
            return FALSE;
        }
    }
}

static u16 SanitizeItemId(u16 itemId)
{
    if (itemId >= ITEMS_COUNT)
        return ITEM_NONE;
    else
        return itemId;
}

const u8 *ItemId_GetName(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].name;
}

u16 ItemId_GetId(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].itemId;
}

u16 ItemId_GetPrice(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].price;
}

u8 ItemId_GetHoldEffect(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].holdEffect;
}

u8 ItemId_GetHoldEffectParam(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].holdEffectParam;
}
u16 ItemIdToBattleMoveId(u16 item);
extern const u8 *const gMoveDescriptionPointers[];
const u8 *ItemId_GetDescription(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].description;
}

u8 ItemId_GetImportance(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].importance;
}

// unused
u8 ItemId_GetUnknownValue(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].unk19;
}

u8 ItemId_GetPocket(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].pocket;
}

u8 ItemId_GetType(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].type;
}

ItemUseFunc ItemId_GetFieldFunc(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].fieldUseFunc;
}

u8 ItemId_GetBattleUsage(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].battleUsage;
}

ItemUseFunc ItemId_GetBattleFunc(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].battleUseFunc;
}

u8 ItemId_GetSecondaryId(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].secondaryId;
}

u16 ItemId_GetArg1(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].arg1;
}

u16 ItemId_GetArg2(u16 itemId)
{
    return gItems[SanitizeItemId(itemId)].arg2;
}

u8 CheckAndGiveKeyItem(void)
{
    if (CheckBagHasItem(gSpecialVar_0x8004, 1) || CheckPCHasItem(gSpecialVar_0x8004, 1))
        return 0;
    else
    {
        if (CheckBagHasSpace(gSpecialVar_0x8004, 1))
        {
            AddBagItem(gSpecialVar_0x8004, 1);
            return 1;
        }
        else
            return 2;
    }
}


void PrepareItemList()
{
    u8 pocket;
    void* temp;
    struct ItemSlot* itemSlotPtr;
    u16 quantity;
    if (gBagQuantity.itemBag != NULL)
        return;
    temp= AllocZeroed(sizeof (struct BagItemQuantity) * POCKETS_COUNT + BAG_ALL_ITEMS_COUNT * sizeof (struct ItemSlot));
    itemSlotPtr = temp + sizeof (struct BagItemQuantity) * POCKETS_COUNT;
    gBagQuantity.itemBag = temp;
    for(pocket = 0; pocket < POCKETS_COUNT; pocket++)
    {
        u32 itemId = 0;
        struct BagItemQuantity* slot = &gBagQuantity.itemBag[pocket];
        slot->itemSlots = itemSlotPtr;
        slot->position = 0;
        for(; itemId < BAG_ALL_ITEMS_COUNT; itemId++)
        {   //道具pocket id在gItems里需要+1;
            quantity = GetBagItemQuantity(itemId);
            if (quantity > 0 && ItemId_GetPocket(itemId) == pocket + 1)
            {
                slot->itemSlots[slot->position].itemId = itemId;
                slot->itemSlots[slot->position].quantity = quantity;
                ++slot->position;
            }
        }
        itemSlotPtr += slot->position;
        slot->capacity = slot->position;
    }

}